/**
 * KopsoxFramework
 * Copyright (C) 2010 Free Software Foundation, Inc. <http://fsf.org/>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kopsox.util.comm;

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.concurrent.CopyOnWriteArraySet;

import org.kopsox.comm.listener.Observer;
import org.kopsox.comm.message.ValueMessage;
import org.kopsox.general.priority.Priority;
import org.kopsox.general.priority.PriorityEnum;

/**
 * This is a utility class that can be used to delegate inform a set of Observers about changes.<br>
 * <b>NOTE:</b>If Performance is more important than the ability to be thread-safe, use the "normal" ObserverSet.<br>
 * 
 * @author Konrad Renner - 08.11.2010
 * @see CopyOnWriteArraySet
 * @see ObserverHashSet
 */
public class CopyOnWriteObserverSet extends CopyOnWriteArraySet<Observer> implements Observer {
	private static final long serialVersionUID = -7387538790511081702L;
	
	private Priority priority;
	private final Comparator<Observer> comparator;
	
	public CopyOnWriteObserverSet() {
		super();
		this.comparator = null;
		init();
	}
	
	public CopyOnWriteObserverSet(Collection<Observer> observers) {
		super(observers);
		this.comparator = null;
		init();
	}
	
	public CopyOnWriteObserverSet(Comparator<Observer> comparator) {
		super();
		this.comparator = comparator;
		init();
	}
	
	private final void init() {
		this.priority = PriorityEnum.MEDIUM.getPriority();
	}

	/* (non-Javadoc)
	 * @see org.kopsox.comm.listener.Observer#notification(org.kopsox.comm.message.ValueMessage)
	 */
	@Override
	public void notification(ValueMessage message) {
		
		//Sorting
		Observer[] observers = super.toArray(new Observer[super.size()]);
		if(this.comparator == null) {
			Arrays.sort(observers);
		}else {
			Arrays.sort(observers,this.comparator);
		}
		
		for(int i=0;i<observers.length;i++) {
			observers[i].notification(message);
		}
	}

	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(final Observer o) {
		if(this.priority.compareTo(o.getPriority()) == 0) {
			return getClass().getName().compareTo(o.getClass().getName());
		}
		return this.priority.compareTo(o.getPriority());
	}

	/* (non-Javadoc)
	 * @see org.kopsox.general.priority.Prioritized#getPriority()
	 */
	@Override
	public Priority getPriority() {
		return this.priority;
	}

	/* (non-Javadoc)
	 * @see org.kopsox.general.priority.Prioritized#setPriority(org.kopsox.general.priority.Priority)
	 */
	@Override
	public void setPriority(final Priority priority) {
		this.priority = priority;
	}

	@Override
    public int hashCode() {
        final int prime = 31;
        int result = super.hashCode();
        result = prime * result
                        + ((comparator == null) ? 0 : comparator.hashCode());
        result = prime * result
                        + ((priority == null) ? 0 : priority.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
                return true;
        if (!super.equals(obj))
                return false;
        if (getClass() != obj.getClass())
                return false;
        CopyOnWriteObserverSet other = (CopyOnWriteObserverSet) obj;
        if (comparator == null) {
                if (other.comparator != null)
                        return false;
        } else if (!comparator.equals(other.comparator))
                return false;
        if (priority == null) {
                if (other.priority != null)
                        return false;
        } else if (!priority.equals(other.priority))
                return false;
        return true;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("CopyOnWriteObserverSet[");
        sb.append("priority=");
        sb.append(priority);
        sb.append(",comparator=");
        sb.append(comparator);
        sb.append(',');
        sb.append(super.toString());
        sb.append(']');
        
        return sb.toString();
    }

}
